#include "ItemManager.h"
#include "GameObjects/Item.h"

#include "StringHelper.h"

#include <iostream>
#include <filesystem>
#include <regex>
#include <algorithm>

ItemManagerPtr ItemManager::singleton;

ItemManagerPtr ItemManager::getSingleton()
{
	if(ItemManager::singleton == nullptr)
		ItemManager::singleton = std::make_shared<ItemManager>();
	return ItemManager::singleton;
}

ItemManager::ItemManager()
{
}

ItemManager::~ItemManager()
{
}

void ItemManager::init(const std::string &path)
{
    std::string item_title;

	std::cout << "Loading items" << std::endl;
	if(std::tr2::sys::is_directory(std::tr2::sys::path(path)))
	{
		for(std::tr2::sys::directory_iterator itr(path); itr!=std::tr2::sys::directory_iterator(); ++itr)
		{
			auto filename = itr->path().filename();
			std::cout << "- " << filename << ": ";
			std::ifstream file(path+"\\"+filename);
			if(file.is_open())
			{
				std::vector<std::string> lines;
				while(file.good())
				{
					std::string line;
					std::getline(file, line);
					if(line.empty()) // Seperation line
						continue;
					if(line[0] == '/' && line[1] == '/') //Comment line
						continue;

                    //Erase an comments in the line
                    auto comment_pos = line.find("//");
                    if(comment_pos != std::string::npos)
                        line = line.substr(0, comment_pos);

					lines.push_back(line);
				}
				file.close();

				//Parse title and description (First two lines)
				item_title = lines[0];
				std::cout << item_title << std::endl;

				auto temp_data = TempItemDataPtr(new TempItemData);

				//Loop over the rest of the lines and parse the text
				unsigned int parse_index = 1;
				while(parse_index < lines.size())
				{
					//DESCRIPTION
					if(StringHelper::toUpper(lines[parse_index], lines[parse_index].size()).find("DESCRIPTION") != std::string::npos)
					{
						parse_index++;
						parseDescription(lines, parse_index, temp_data->description);
					}
					//SETTINGS
					else if(StringHelper::toUpper(lines[parse_index], lines[parse_index].size()).find("SETTINGS") != std::string::npos)
					{
						parse_index++;
						parseSettings(lines, parse_index, temp_data->settings);
					}
					//SPECIALS

					//KEEP LOOP ALIVE
					else
					{
						parse_index++;
					}
				}

				
				auto item = std::make_shared<Item>(item_title, temp_data->description);
				unsigned int item_id = (unsigned int)atoi((filename.substr(0, filename.find_last_of("."))).c_str());
				items[item_id] = item;
				temp_item_data[item_id] = std::move(temp_data);
			}
			else
			{
				std::cout << "Unable to open file!" << std::endl;
			}
		}
	}
	else 
	{
		std::cout << (std::tr2::sys::exists(std::tr2::sys::path(path)) ? "Found: " : "Not found: ") << path << '\n';
	}

	//Apply parsed data and settings
	for(auto it = items.begin(); it != items.end(); ++it)
	{
		applySettings(it);
	}

	std::cout << "done." << std::endl;
}

///////////////////////////////////
// DESCRIPTION PARSING
///////////////////////////////////
void ItemManager::parseDescription(const std::vector<std::string> &lines, unsigned int &index, std::string &description)
{
	while(index < lines.size())
	{
		//Ignore start bracket
		if(lines[index].find("{") != std::string::npos)
		{
			index++;
			continue;
		}

		//Finish parsing at end bracket
		if(lines[index].find("}") != std::string::npos)
		{
			index++;
			description.erase(std::remove(begin(description), end(description), '\t'), end(description));
			return;
		}

		std::string line = lines[index++];
		
		description += " " + line;
	}

	std::cout << "ERROR: OUT OF BOUNDS WHEN PARSING ITEM DESCRIPTION FOR " << lines[0] << "!" << std::endl;
}

///////////////////////////////////
// SETTINGS PARSING
///////////////////////////////////
void ItemManager::parseSettings(const std::vector<std::string> &lines, unsigned int &index, std::vector<unsigned int> &settings)
{
	while(index < lines.size())
	{
		//Ignore start bracket
		if(lines[index].find("{") != std::string::npos)
		{
			index++;
			continue;
		}

		//Finish parsing at end bracket
		if(lines[index].find("}") != std::string::npos)
		{
			index++;
			return;
		}

		//Otherwise, we parse the line as a setting
		const std::string &setting = lines[index++];

        //Check for ITEM settings
		if(StringHelper::toUpper(setting, setting.size()).find("NON_INTERACTIVE") != std::string::npos)
			settings.push_back(Item::NON_INTERACTIVE);
        else if(StringHelper::toUpper(setting, setting.size()).find("INDEF_ARTICLE_AN") != std::string::npos)
			settings.push_back(Item::INDEF_ARTICLE_AN);
        else if(StringHelper::toUpper(setting, setting.size()).find("STANCE_STAND") != std::string::npos)
			settings.push_back(Item::STANCE_STAND);
        else if(StringHelper::toUpper(setting, setting.size()).find("STANCE_HOVER") != std::string::npos)
			settings.push_back(Item::STANCE_HOVER);
        else if(StringHelper::toUpper(setting, setting.size()).find("STANCE_GROW") != std::string::npos)
			settings.push_back(Item::STANCE_GROW);
	}

	std::cout << "ERROR: OUT OF BOUNDS WHEN PARSING ITEM SETTINGS FOR " << lines[0] << "!" << std::endl;
}

void ItemManager::applySettings(std::unordered_map<unsigned int, ItemPtr>::iterator &it)
{
	auto temp_it = temp_item_data.find(it->first);
	if(temp_it == temp_item_data.end())
		return;

	for(unsigned int i = 0; i < temp_it->second->settings.size(); i++)
	{
		it->second->add(static_cast<Item::Setting>(temp_it->second->settings[i]));
	}
}

const ItemPtr &ItemManager::get(unsigned int item_id) const
{
    auto it = items.find(item_id);
    if(it != items.end())
        return it->second;
    else
        return null_item;
}
